package com.example.java_gobang.api;

import com.example.java_gobang.game.OnlineUserManager;
import com.example.java_gobang.game.Room;
import com.example.java_gobang.mapper.UserMapper;
import com.example.java_gobang.model.User;
import com.example.java_gobang.service.RoomService;
import com.example.java_gobang.tools.Constant;
import com.example.java_gobang.tools.GameReadyResponse;
import com.example.java_gobang.tools.GameResponse;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.util.Random;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: Lenovo
 * Date: 2022-08-17
 * Time: 15:59
 */
@Component
public class GameAPI extends TextWebSocketHandler {
    private ObjectMapper objectMapper = new ObjectMapper();

    @Autowired
    private RoomService roomService;

    @Autowired
    private OnlineUserManager onlineUserManager;

    @Autowired
    private UserMapper userMapper;

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        GameReadyResponse gameReadyResponse = new GameReadyResponse();
        //1、获取道用户的身份信息 httpsession

        User user = (User) session.getAttributes().get(Constant.USERINFO_SESSION_KEY);
        if(user == null){
            gameReadyResponse.setOk(false);
            gameReadyResponse.setReason("用户尚未登录");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(gameReadyResponse)));
            return;
        }

        //2.判定当前用户是否已经进入房间 房间管理器进行查询
        Room room = roomService.getRoomBYUserId(user.getUserId());
        if(room == null){
            //如果为空 当前没有找到对应房间 该玩家还没有匹配道
            gameReadyResponse.setOk(false);
            gameReadyResponse.setReason("当前用户尚未匹配到");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(gameReadyResponse)));
            return;
        }

        //3.判定当前用户是不是多开 该用户是否在其他地方进入游戏
        //通过 onlineMananger 如果一个账号 一边是在游戏大厅 一边是在游戏房间

        if(onlineUserManager.getFromGameHall(user.getUserId()) != null
                || onlineUserManager.getFromGameRoom(user.getUserId()) != null){
            gameReadyResponse.setOk(true);
            gameReadyResponse.setReason("禁止游戏多开");
            gameReadyResponse.setMessage("repeatConnection");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(gameReadyResponse)));
            return;
        }

        //4.设置当前玩家上线
        onlineUserManager.enterGameRoom(user.getUserId(), session);

        //5.把两个玩家加入房间中
        //执行到当前逻辑证明页面跳转成功
        //两个客户端并发的连入服务器
        synchronized (room){
            if(room.getUser1() == null){
                //第一个玩家尚未加入房间
                //就把当前连上的 websocket 玩家作为 user 加入到房间中
                room.setUser1(user);
//                //先进入房间的是先手
//                room.setWhiteUser(user.getUserId());

                // TODO:
                //生成两个随机
                //生成一个 0-1 的随机数
                Random random = new Random();
                int whiteNum = random.nextInt(2);
                room.setWhiteUser(whiteNum);
                //如果生成的是 1 就是玩家 1 白
                //如果生成的是 0 就是玩家 2 白

                System.out.println("玩家" + user.getUsername() + "已经准备就绪");

                return;
            }

            if(room.getUser2() == null){
                //玩家1已经加入房间 把当前玩家当作 玩家2
                room.setUser2(user);
                System.out.println("玩家" + user.getUsername() + "已经准备就绪");

                //当两个玩家都加入成功后 服务器给这两个玩家 返回 websocket 响应数据通知这两个玩家游戏双方都已经准备好了

                //通知玩家1
                noticeGameReady(room,room.getUser1(),room.getUser2());

                if(room.getWhiteUser() == 1){
                    room.setWhiteUser(0);
                }else{
                    room.setWhiteUser(1);
                }
                //通知玩家2
                noticeGameReady(room,room.getUser2(),room.getUser1());

                return;
            }
        }

        //6.如果又有玩家尝试连接同一个房间 提示出错 这种情况理论上是不存在的 为了让程序更加健壮
        gameReadyResponse.setOk(false);
        gameReadyResponse.setReason("当前房间已满，您不能加入");

        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(gameReadyResponse)));
    }

    private void noticeGameReady(Room room, User thisUser, User thatUser) throws IOException {
        GameReadyResponse gameReadyResponse = new GameReadyResponse();
        gameReadyResponse.setOk(true);
        gameReadyResponse.setReason("");
        gameReadyResponse.setMessage("gameReady");
        gameReadyResponse.setRoomId(room.getRoomId());
        gameReadyResponse.setThisUserId(thisUser.getUserId());
        gameReadyResponse.setThatUserId(thatUser.getUserId());
        gameReadyResponse.setIsWhite(room.getWhiteUser());

        WebSocketSession socketSession = onlineUserManager.getFromGameRoom(thisUser.getUserId());
        socketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(gameReadyResponse)));
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        User user = (User) session.getAttributes().get(Constant.USERINFO_SESSION_KEY);

        if(user == null){
            System.out.println("当前玩家尚未登录");
            return;
        }

        //根据玩家 id 获取到房间 对象
        Room room = roomService.getRoomBYUserId(user.getUserId());
        room.putChess(message.getPayload());
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        User user = (User) session.getAttributes().get(Constant.USERINFO_SESSION_KEY);

        if(user == null){
            return;
        }

        WebSocketSession exitSession = onlineUserManager.getFromGameRoom(user.getUserId());
        //避免在多开后 第二个退出登录 第一个会话被删除
        if(exitSession == session){
            onlineUserManager.exitGameRoom(user.getUserId());
        }
        System.out.println("当前用户" + user.getUsername() + "游戏房间连接异常");
        noticeThatUserWin(user);
    }


    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        User user = (User) session.getAttributes().get(Constant.USERINFO_SESSION_KEY);

        if(user == null){
            return;
        }

        WebSocketSession exitSession = onlineUserManager.getFromGameRoom(user.getUserId());
        //避免在多开后 第二个退出登录 第一个会话被删除
        if(exitSession == session){
            onlineUserManager.exitGameRoom(user.getUserId());
        }
        System.out.println("当前用户" + user.getUsername() + "离开游戏房间");
        noticeThatUserWin(user);
    }

    private void noticeThatUserWin(User user) throws IOException {
        //根据当前玩家找到房间
        Room room = roomService.getRoomBYUserId(user.getUserId());

        if(room == null){
            //当前房间已被释放 即没有队手
            System.out.println("当前房间已经释放，无需通知队手");
            return;
        }

        //根据房间找到队手
        User thatUser = (user == room.getUser1()) ? room.getUser2() : room.getUser1();

        //找到队手的在线状态
        WebSocketSession socketSession = onlineUserManager.getFromGameRoom(thatUser.getUserId());
        if(socketSession == null){
            //双方都掉线了
            System.out.println("队手也已经掉线");
            return;
        }

        //构造响应你是获胜方

        GameResponse response = new GameResponse();
        response.setMessage("outOfRoom");
        response.setUserId(thatUser.getUserId());
        response.setWinner(thatUser.getUserId());

        socketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
        //更新玩家信息
        int winUserId = thatUser.getUserId();
        int loseUserId = user.getUserId();

        userMapper.userLose(loseUserId);
        userMapper.userWin(winUserId);

        //释放房间
        roomService.remove(room.getRoomId(),room.getUser1().getUserId(),room.getUser2().getUserId());
    }
}
